package Thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 原子引用+时间戳来解决CAS 引出的ABA问题
 */
public class CAS_DEMO {
    static class Person {
        String userName;
        int age;

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Person(String userName, int age) {
            this.userName = userName;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    //普通原子引用
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    //具有时间戳的原子引用     初始化值  版本号
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {

        //普通原子引用
        // AtomicReferenceDemo();

        //普通引用具有ABA问题
        // System.out.println("以下是ABA问题的编程：：");
        // ABAAtomicDemo();
        //解决ABA问题   时间戳+原子引用
        System.out.println("解决ABA问题   时间戳+原子引用");
        ABADemoSolutation();

    }

    //ABA问题的Demo 这里t2线程修改的值其实是t1线程改过又该回来的值   可以看出来，如果没有带有版本号的原子引用，就修改成功了
    private static void ABADemoSolutation() {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("t1初始版本号：" + stamp);
            //暂存一秒钟
            stopTime(1);
            atomicStampedReference.compareAndSet(100, 101, stamp, 2);
            stamp = atomicStampedReference.getStamp();
            System.out.println("t1第一次修改后得版本号：" + stamp);
            atomicStampedReference.compareAndSet(101, 100, stamp, 3);
            stamp = atomicStampedReference.getStamp();
            System.out.println("t1第二次修改后得版本号：" + stamp);
        }, "t1").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("t2初始版本号：" + stamp);
            //暂停3秒钟
            stopTime(3);
            System.out.println("暂停三秒后，其实之前的版本号已经增加了，不是原来的版本号了，现在t2版本号："+ atomicStampedReference.getStamp());
            System.out.println("当前实际值是："+atomicStampedReference.getReference());
            boolean b = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println("t2是否修改成功："+b);
        }, "t2").start();

        System.out.println("main线程完成:" + Thread.currentThread().getName());
    }

    private static void stopTime(int time) {
        try {
            //暂停1S钟 保证上面t1线程完成ABA操作
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }


    //ABA问题的Demo 这里t2线程修改的值其实是t1线程改过又该回来的值
    private static void ABAAtomicDemo() {
        new Thread(() -> {
            atomicReference.compareAndSet(100, 101);
            Integer integer = atomicReference.get();
            System.out.println("第一次比较并交换" + integer);
            atomicReference.compareAndSet(101, 100);
            Integer integer1 = atomicReference.get();
            System.out.println("第一次比较并交换" + integer1);
        }, "t1").start();

        new Thread(() -> {
            try {
                //暂停1S钟 保证上面t1线程完成ABA操作
                TimeUnit.SECONDS.sleep(1);

                atomicReference.compareAndSet(100, 2019);
                Integer integer1 = atomicReference.get();
                System.out.println("第一次比较并交换" + integer1);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }, "t2").start();

        System.out.println("main线程完成:" + Thread.currentThread().getName());
    }

    /**
     * 原子引用类：AtomicReference 对引用对象进行包装
     */
    private static void AtomicReferenceDemo() {
        AtomicInteger atomicInteger = new AtomicInteger();
        Person zhangsan = new Person("z3", 12);
        Person lisi = new Person("L4", 13);

        AtomicReference<Person> atomicReference = new AtomicReference<>();
        atomicReference.set(zhangsan);
        System.out.println(atomicReference.get());

        System.out.println(atomicReference.compareAndSet(zhangsan, lisi));

        System.out.println(atomicReference.get());
    }
}
